博客中代码地址:https://github.com/farliu/farpc.git
直至上一章,手写dubbo全部介绍完了,这一章作为番外篇,了解如何使用内嵌tomcat。
项目结构介绍 本节涉及博客中代码的module,farpc-rpc(远程调用)。
内嵌tomcat使用 使用tomcat,自然就是http协议,我们先导入tomcat的依赖。
1 2 3 4 5 <dependency > <groupId > org.apache.tomcat.embed</groupId > <artifactId > tomcat-embed-core</artifactId > <version > 9.0.27</version > </dependency >
继续实现之前留下的扩展接口IConsumerServer、IProviderServer。在实现IProviderServer之前,我们需要先得到一个Servlet用于处理请求。
1 2 3 4 5 6 7 public class DispatcherServlet extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException , IOException { new HttpServerHandler ().handle(req, resp); } }
为了方便扩展,我在servlet中调用定义的Handler,后续如果需要增加Handler,也只要修改这里就行了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public class HttpServerHandler { private static final Logger logger = LoggerFactory . getLogger(HttpServerHandler.class ) ; public void handle(HttpServletRequest req, HttpServletResponse resp) { try { ServletInputStream inputStream = req.getInputStream() ; ObjectInputStream objectInputStream = new ObjectInputStream(inputStream ) ; RequestDTO requestDTO = (RequestDTO) objectInputStream.readObject() ; Object result = new Object() ; logger.info("receive request.. {}" , requestDTO); if (Container . getProviders() .containsKey(requestDTO .getClassName () )) { Object provider = Container . getProviders() .get(requestDTO.getClassName() ); Class<?> providerClazz = provider.getClass() ; Method method = providerClazz.getMethod(requestDTO .getMethodName () , requestDTO.getTypes() ); result = method .invoke(provider, requestDTO.getParams() ); } ObjectOutputStream objectOutputStream = new ObjectOutputStream(resp .getOutputStream () ); objectOutputStream.writeObject(result ) ; objectOutputStream.flush() ; objectOutputStream.close() ; } catch (Exception e) { } } }
Handler的代码也很容易理解,就是从Request中获得请求参数,然后根据请求参数反射执行对应的方法,然后输出到输出流中。
对于IConsumerServer的实现,就是Http调用了,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public class HttpConsumerServer implements IConsumerServer { public Object execute(String address, RequestDTO requestDTO) { String[] addrs = address.split(":" ); String ip = addrs[0 ] ; Integer port = Integer . parseInt(addrs [1]) ; try { URL url = new URL("http" , ip , port , "/" ) ; HttpURLConnection httpURLConnection = (HttpURLConnection) url.open Connection() ; httpURLConnection.setRequestMethod("POST" ) ; httpURLConnection.setDoOutput(true ) ; httpURLConnection.setDoInput(true ) ; OutputStream outputStream = httpURLConnection.getOutputStream() ; ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream ) ; objectOutputStream.writeObject(requestDTO ) ; objectOutputStream.flush() ; objectOutputStream.close() ; InputStream inputStream = httpURLConnection.getInputStream() ; ObjectInputStream objectInputStream = new ObjectInputStream(inputStream ) ; Object result = objectInputStream.readObject() ; return result; } catch (Exception e) { e.printStackTrace() ; } return null; } }
还有别忘了增加SPI扩展点
1 2 3 http=com.ofcoder .farpc .rpc .http .HttpConsumerServer http=com.ofcoder .farpc .rpc .http .HttpProviderServer
测试 测试代码位于farpc-demo的模块下。
1 2 3 4 5 6 7 8 9 10 11 12 13 @Test public void providerTest() throws IOException { IProviderServer server = RpcFactory . getProviderServer() ; server.start("127.0.0.1:20880" ); System .in .read() ; } @Test public void consumerTest() { IWelcome welcome = ConsumerProxy . create(IWelcome .class ); String far = welcome.greet("far" ); System . out.println(far); }
可以分别看到相应的日志。服务提供端日志如下:
1 2 3 4 5 6 7 8 9 ... 十月 28, 2019 9:55 :57 下午 org .apache .coyote .AbstractProtocol init 信息: Initializing ProtocolHandler ["http-nio-20880" ] 十月 28, 2019 9:55 :57 下午 org .apache .catalina .core .StandardService startInternal 信息: Starting service [Tomcat] 十月 28, 2019 9:55 :57 下午 org .apache .catalina .core .StandardEngine startInternal 信息: Starting Servlet engine : [Apache Tomcat/9.0.27] 十月 28, 2019 9:55 :57 下午 org .apache .coyote .AbstractProtocol start 信息: Starting ProtocolHandler ["http-nio-20880" ]
消费端:
1 2 3 ... [28 /10 /19 21 :56 :56 :191 CST] main-EventThread INFO state.ConnectionStateManager: State change: CONNECTED hello far, welcome to ofcoder.